﻿using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using DeepEarth.Map.Core.Utilities;
using System;
using DeepEarth.Core.Utilities;
using DeepEarth.Map.Core;
using System.Linq;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using DeepEarth.Core;
using DeepEarth.Core.Data;
using DeepEarth.Map.Core.Filters;
using DeepEarth.Map.Core.Clustering;
namespace DeepEarth.Map.Core
{
    public class DynamicLayer : UIElementLayer
    {
        private Synchronizer synchronizer;
        private Synchronizer Synchronizer
        {
            get
            {
                if (synchronizer == null)
                {
                    if (MapInstance != null)
                    {
                        synchronizer = new Synchronizer(MapInstance.LocationToViewportPoint);
                    }
                }
                return synchronizer;
            }
            set
            {
                synchronizer = value;
            }
        }

        public DynamicLayer()
        {
            HorizontalAlignment = HorizontalAlignment.Stretch;
            VerticalAlignment = VerticalAlignment.Stretch;

            this.LayoutUpdated += DynamicLayer_LayoutUpdated;            
        }

        void DynamicLayer_LayoutUpdated(object sender, EventArgs e)
        {
            if (MapInstance != null)
            {
                EnsureSynchronization();
                this.LayoutUpdated -= DynamicLayer_LayoutUpdated;
            }
        }

        void EnsureSynchronization()
        {
            Synchronizer = new Synchronizer(MapInstance.LocationToViewportPoint);
            SizeChanged += (o, x) => SynchronizeOnFrame();
            SynchronizeOnFrame();            
        }

        protected override void MapChanged()
        {
            Children.OfType<ISelfPositioning>().Iter(s => s.LocationToPoint = MapInstance.LocationToViewportPoint);
        }

        public override void Add(GeoItemViewModel viewModel, UIElement element)
        {
            base.Add(viewModel, element);
            InitializeSync(element);
        }

        public void Add(UIElement element)
        {
            this.Children.Add(element);
            InitializeSync(element);
        }

        protected void InitializeSync(UIElement element)
        {           
            FrameworkElement fe = element as FrameworkElement;

            if (fe != null)
            {
                // If the element is a pushpin (for example) and it loads its icon asynchronously,
                // it will resize unexpectedly. Sync when this happens.
                // Scale changes don't fire this event - which is nice.

                WeakEventListener<DynamicLayer, FrameworkElement> wel = new WeakEventListener<DynamicLayer, FrameworkElement>(this, fe);
                fe.SizeChanged += wel.OnEvent;

                wel.DetatchAction = (listener, source) =>
                {
                    source.SizeChanged -= listener.OnEvent;
                };

                wel.EventAction = (listener, source, eventArgs) =>
                {
                    listener.SynchronizeElement(source as UIElement);
                };
            }

            ISelfPositioning sp = element as ISelfPositioning;
            if (sp != null && MapInstance != null)
            {
                sp.LocationToPoint = MapInstance.LocationToViewportPoint;
            }
        }

        public override void Remove(GeoItemViewModel viewModel, UIElement element)
        {
            this.Children.Remove(element);

            var removedItems = this.referenceElements.Keys.Where(r => r.WrappedViewModel == viewModel).ToList();

            foreach (var r in removedItems)
            {
                UIElement e = this.referenceElements[r];
                this.Children.Remove(e);
                this.referenceElements.Remove(r);
            }
        }

        //synchronize childrens location or if shape, locations, with map
        protected override void SynchronizeOnFrame()
        {
            SynchronizeOffsets();

            if (Children.Count > 0 && IsVisible && MapInstance != null)
            {
                this.Children.VisibleElements.Iter(c => SynchronizeElement(c));
                this.Children.UnboundItems.Where(c => c.Visibility == Visibility.Visible).Iter(c => SynchronizeElement(c));
            }
        }

        protected override void SynchronizeOnEnd()
        {
            SynchronizeOnFrame();
        }

        public void SynchronizeElement(UIElement element)
        {
            Synchronizer.SynchronizeElement(element, MapLogicalWidth);
        }
    }
}
